<?php
/**
 * bs多规格sku商城系统
 * ============================================================================
 * 版权所有 2024-2099 wltx666.com，并保留所有权利。
 * 网站地址: https://wltx666.com
 * ============================================================================
 * Author: 草上飞
 * Email: wltx@alipay168.cn
 * QQ: 2719875001
 * Date: 2025/3/2
 * 描述:
 */

namespace app\service;


use app\model\auto\RechargeLog;
use app\model\goods\Goods;
use app\model\order\Order;
use app\model\order\OrderGoods;
use app\result\order\OrderResult;
use app\result\SettingResult;
use fengkui\Pay\Alipay;
use fengkui\Pay\Wechat;

class PaymentService
{

    use BaseService;

    private function getAlipayConfig($args = [])
    {
        $config = config('payment.alipay');

        $settingConf = (new SettingResult())->get_value('payment');
        $bs_from = (defined('BS_FROM') ? BS_FROM : 'h5');
        if (!empty($args['bs_from'])) {
            $bs_from = $args['bs_from'];
        }

        if ($bs_from == 'app') {
            if (!empty($settingConf['alipay_app_appid'])) {
                $config['app_id'] = $settingConf['alipay_app_appid'];
            }
            if (!empty($settingConf['alipay_app_public_key'])) {
                $config['public_key_ali'] = $settingConf['alipay_app_public_key'];
            }
            if (!empty($settingConf['alipay_app_private_key'])) {
                $config['private_key'] = $settingConf['alipay_app_private_key'];
            }
        } else {
            if (!empty($settingConf['alipay_appid'])) {
                $config['app_id'] = $settingConf['alipay_appid'];
            }
            if (!empty($settingConf['alipay_public_key'])) {
                $config['public_key_ali'] = $settingConf['alipay_public_key'];
            }
            if (!empty($settingConf['alipay_private_key'])) {
                $config['private_key'] = $settingConf['alipay_private_key'];
            }
        }

        $alipayConfig = [
            'app_id' => trim($config['app_id']), // 开发者的应用ID
            'xcx_id' => $config['xcx_id'] ?? "", // 小程序 appid
            //'public_key' => trim($config['public_key']), // 支付宝公钥，一行字符串
            'public_key' => $config['public_key_ali'], // 支付宝公钥，一行字符串
            'private_key' => trim($config['private_key']), // 开发者私钥去头去尾去回车，一行字符串

            'notify_url' => $config['notify_url'], // 异步接收支付状态
            'return_url' => $args['return_url'] ?? $config['return_url'], // 返回地址
            'sign_type' => 'RSA2', // 生成签名字符串所使用的签名算法类型，目前支持RSA2和RSA，默认使用RSA2
            'is_sandbox' => false, // 是否使用沙箱调试，true使用沙箱，false不使用，
        ];
        bs_log($alipayConfig, 'info', 'alipay_config.log');
        return $alipayConfig;
    }

    public function getWechatConfig($args = []): array
    {
        $settingConf = (new SettingResult())->get_value('payment');
        //证书生成文件
        $toPath = app()->getRuntimePath() . 'cert/';
        if (!is_dir($toPath)) {
            @mkdir($toPath, 0777, true);
        }

        //服务商模式
        if (!empty($settingConf['wechat_sp_open'])) {
            if (!is_file($toPath . 'wechat_sp_cert_client.pem')) {
                @file_put_contents($toPath . 'wechat_sp_cert_client.pem', $settingConf['wechat_sp_cert_client']);
            }
            if (!is_file($toPath . 'wechat_sp_cert_key.pem')) {
                @file_put_contents($toPath . 'wechat_sp_cert_key.pem', $settingConf['wechat_sp_cert_key']);
            }
        } else {
            if (!is_file($toPath . 'wechat_public_key.pem')) {
                @file_put_contents($toPath . 'wechat_public_key.pem', $settingConf['wechat_public_key']);
            }
        }
        $bs_from = (defined('BS_FROM') ? BS_FROM : 'h5');
        if (!empty($args['bs_from'])) {
            $bs_from = $args['bs_from'];
        }
        if ($bs_from == 'app') {
            $appid = $settingConf['wechat_app_appid'];
            $app_secret = $settingConf['wechat_app_secret'];
        } elseif ($bs_from == 'xcx') {
            $appid = $settingConf['wechat_xcx_appid'];
            $app_secret = $settingConf['wechat_xcx_secret'];
        } else {
            $appid = $settingConf['wechat_gzh_appid'];
            $app_secret = $settingConf['wechat_gzh_secret'];
        }
        # 微信支付配置
        $wechatConfig = [
            'xcxid' => $settingConf['wechat_xcx_appid'] ?? '', // 小程序 appid
            'appid' => $appid, // 微信支付 appid
            'appsecret' => $app_secret,
            'mchid' => $settingConf['wechat_mchid'] ?? '', // 微信支付 mch_id 商户收款账号
            'key' => $settingConf['wechat_mchkey'] ?? '', // 微信支付 apiV3key（尽量包含大小写字母，否则验签不通过，服务商模式使用服务商key）


            'sp_appid' => $settingConf['wechat_sp_appid'] ?? '', // 服务商应用 ID
            'sp_mchid' => $settingConf['wechat_sp_mchid'] ?? '', // 服务商户号

            'notify_url' => request()->domain() . '/api/payment/notify/wechat', // 接收支付状态的连接  改成自己的回调地址
            'redirect_url' => '', // 公众号支付，调起支付页面, todo 发起时动态化

            // 服务商模式下，使用服务商证书
            'serial_no' => $settingConf['wechat_sp_serial_no'] ?? '', // 商户API证书序列号（可不传，默认根据证书直接获取）
            'cert_client' => $toPath . 'wechat_sp_cert_client.pem', // 证书（退款，红包时使用）
            'cert_key' => $toPath . 'wechat_sp_cert_key.pem', // 商户API证书私钥（Api安全中下载）

            'public_key_id' => $settingConf['wechat_public_key_id'] ?? '', // 平台证书序列号或支付公钥ID
            // （支付公钥ID请带：PUB_KEY_ID_ 前缀，默认根据证书直接获取，不带前缀）
            'public_key' => $toPath . 'wechat_public_key.pem', // 平台证书或支付公钥（Api安全中下载）
            // （微信支付新申请的，已不支持平台证书，老版调用证书列表，自动生成平台证书，注意目录权限）
        ];


        return $wechatConfig;
    }


    // 支付宝
    public function alipay($args)
    {
        $out_trade_no = $args['order_sn'] ?? '';
        $total_amount = $args['order_amount'] ?? 0;
        $subject = $args['subject'] ?? '';
        $user_id = $args['user_id'] ?? 0;
        $return_url = $args['return_url'] ?? '';
        if (!$return_url) {
            if (str_starts_with($out_trade_no, 'REC')) {
                $return_url = config('domain.domains.h5') . '/#/pages/packageA/user/center';
            } else {
                $return_url = config('domain.domains.h5') . '/#/pages/packageA/order/detail?order_sn=' . $out_trade_no;
            }
            $args['return_url'] = $return_url;
        }
        $alipayConfig = $this->getAlipayConfig($args);

        //bs_log(print_r($alipayConfig, true), 'info', 'alipay_config.log');
        $pay = new Alipay($alipayConfig);

        $order = [
            'body' => $subject, // 商品描述
            'order_sn' => $out_trade_no, // 商户订单号
            'total_amount' => $total_amount, // 订单金额
        ];
        $bsFrom = defined('BS_FROM') ? BS_FROM : ($args['bs_from'] ?? 'h5');
        if ($bsFrom == 'app') {
            $result = $pay->app($order);
        } elseif ($bsFrom == 'web') {
            //fixme 扫码支付
            $pay = new Alipay($alipayConfig);

            $result = $pay->web($order); // GET请求（默认 $pay->web($order, 'get')）

            $result = $pay->web($order, 'POST'); // POST请求，构建html表单，直接跳转链接
            echo $result;

            // 获取支付宝支付二维码，使用扫码支付
            $order['qr_pay_mode'] = 4;
            $result = $pay->web($order); // 获取到二维码图片

        } else {
            $result = $pay->wap($order, 'post');
        }
        return $result;
    }

    public function refund_alipay($args)
    {
        //todo 退款配置
        $alipayConfig = $this->getAlipayConfig(['bs_from' => 'admin']);

        $alipay = new Alipay($alipayConfig);
        try {
            $res = $alipay->refund([
                'order_sn' => $args['order_sn'],
                'refund_sn' => $args['refund_sn'],
                'trade_no' => $args['trade_no'],
                'refund_amount' => $args['refund_amount'],
                'reason' => $args['reason'],
            ]);
        } catch (\Exception $e) {
            bs_log($e->getMessage(), 'error', 'alipay_refund.log');
            return self::error_return($e->getMessage());
        }
        if ($res) {
            return self::success_return('退款成功');
        }
        return self::error_return('退款失败');

    }

    // 微信退款
    public function refund_wechat($args)
    {
        $pay = new Wechat($this->getWechatConfig($args));

        $order = [
            'refund_sn' => time(), // 商户退款单号
            'refund_amount' => 1, // 退款金额
            'order_sn' => 'codexxxxxxxx', # 商户订单号（order_sn或transaction_id两者必传其一）
            'transaction_id' => 'openidxxxxxxxx', // 微信支付订单号
        ];

        $result = $pay->refund($order);
        // 返回 Collection 实例包含退款相关信息
        // $order['reason'] // 退款原因选填

        return $result;
    }


    // 微信支付
    public function wechat($args)
    {
        $pay = new Wechat($this->getWechatConfig($args));

        $order = [
            'body' => $args['subject'] ?? '支付订单', // 商品描述
            'order_sn' => $args['order_sn'], // 商户订单号
            'total_amount' => $args['order_amount'], // 订单金额
        ];
        $bsFrom = defined('BS_FROM') ? BS_FROM : ($args['bs_from'] ?? 'h5');

        if ($bsFrom == 'app') {
            $res = $pay->js($order);
            $result = [
                'params' => $res,
            ];
        } elseif ($bsFrom == 'h5') {
            //H5支付
            $order['type'] = 'wap';
            $order['redirect_url'] = '';//todo 返回URL，必填
            $h5_url = $pay->h5($order);
            $result = [
                'h5_url' => $h5_url,
            ];
        } elseif ($bsFrom == 'gzh') {
            $order = array_merge($order, [
                'code' => 'codexxxxxxxx', // 用户登录凭证（code或openid两者必传其一）
                'openid' => 'openidxxxxxxxx', // 用户唯一标识
            ]);
            $order['redirect_url'] = '';//todo 返回URL，必填
            $res = $pay->js($order);
            $result = [
                'params' => $res,
            ];
        } elseif ($bsFrom == 'xcx') {
            $order = array_merge($order, [
                'openid' => 'openidxxxxxxxx', // 用户唯一标识
            ]);
            $res = $pay->xcx($order);
            $result = [
                'params' => $res,
            ];
        } else {
            //扫码
            $order['redirect_url'] = '';//todo 返回URL，必填
            $code_url = $pay->scan($order);
            $result = [
                'code_url' => $code_url,
            ];
        }

        return self::success_return('success', $result);

    }


    public function query_wechat($args)
    {
        $pay = new Wechat($this->getWechatConfig($args));
        if (!empty($args['trade_no'])) {
            $result = $pay->query($args['trade_no'], true); // 商户订单号查询订单
        } else {
            $result = $pay->query($args['order_sn'], true);
        }
        return self::success_return('success', $result);
    }

    public function query_alipay($args)
    {
        $pay = new Alipay($this->getAlipayConfig($args));
        if (!empty($args['trade_no'])) {
            $result = $pay->query($args['trade_no'], true); // 支付宝支付订单号查询订单
        } else {
            $result = $pay->query($args['order_sn'], true);
        }
        return self::success_return('success', $result);
    }

    public function wechat_callback($response = [])
    {
        bs_log($response, 'response', 'wechat_notify.log');
        //{
        //    "id": "EV-2018022511223320873",
        //    "create_time": "2015-05-20T13:29:35+08:00",
        //    "resource_type": "encrypt-resource",
        //    "event_type": "TRANSACTION.SUCCESS",
        //    "summary": "支付成功",
        //    "resource": {
        //        "original_type": "transaction",
        //        "algorithm": "AEAD_AES_256_GCM",
        //        "ciphertext": "",
        //        "associated_data": "",
        //        "nonce": ""
        //    }
        //}
        $pay = new Wechat($this->getWechatConfig());

        // 支付及转账回调
        try {
            $data = $pay->notify(); // 验签并获取支付及转账回调相关参数
            if ($data) { // 支付成功
                bs_log($data, 'success', 'wechat_notify.log');
                $order_sn = $data['out_trade_no'];
                $trans_no = $data['transaction_id'];
                $pay_time = $data['success_time'] ?? date('Y-m-d H:i:s');
                $pay_price = bcdiv($data['amount']['payer_total'], 100, 2);//单位是分
                $trade_type = $data['trade_type'];//JSAPI：公众号支付、小程序支付 NATIVE：Native支付 APP：APP支付 MICROPAY：付款码支付 MWEB：H5支付 FACEPAY：刷脸支付
                $appid = $data['appid'];
                $openid = $data['payer']['openid'];
                $this->callback_logic($order_sn, $trans_no, $pay_time, $pay_price, $data);
                //$pay->success(); // 异步通知
            }
        } catch (\Exception $e) {
            $e->getMessage(); // 错误信息
        }
        // 当回调数据为空时，请打印错误信息
        // $pay->notify(false); 不验证签名，直接返回支付信息

    }

    //支付回调
    public function alipay_callback($response = [])
    {
        $alipayConfig = $this->getAlipayConfig();
        //bs_log($response, 'response', 'alipay_notify.log');
        bs_log($_POST, '_POST', 'alipay_notify.log');

        $pay = new Alipay($alipayConfig);
        //bs_log($alipayConfig, 'alipayConfig', 'alipay_config.log');
        try {
            $data = $pay->notify($_POST); // 验签并获取支付回调相关参数
            bs_log([$data], 'success', 'alipay_notify.log');
            if ($data) {
                // 支付成功
                //bs_log($data, 'success', 'alipay_notify.log');
                $order_sn = $data['out_trade_no'] ?? "";
                $trans_no = $data['trade_no'] ?? "";
                $pay_time = $data['gmt_payment'] ?? date('Y-m-d H:i:s');
                $pay_price = !empty($data['buyer_pay_amount']) ? $data['buyer_pay_amount'] : $data['receipt_amount'];

                $this->callback_logic($order_sn, $trans_no, $pay_time, $pay_price, $data);
                echo "success";
                die;
            }
        } catch (\Exception $e) {
            $e->getMessage(); // 错误信息
            bs_log($e->getMessage(), 'error', 'alipay_notify.log');
        }
        echo "fail";
        die;
    }

    private function callback_logic($order_sn, $trans_no, $pay_time, $pay_price, $from = 'alipay', $rawData = [])
    {
        $payType = $from == 'alipay' ? 2 : 1;
        if (str_starts_with($order_sn, 'REC')) {
            $order = (new RechargeLog())->data_get(['order_sn' => $order_sn]);
            if (!$order) {
                return false;
            }
            if ($order['pay_status'] == 1) {
                return true;
            }
            $res = (new RechargeLog())->data_update(['order_sn' => $order_sn], [
                'pay_status' => 1,
                'pay_time' => $pay_time,
                'check_status' => 1,
                'check_time' => date('Y-m-d H:i:s'),
                'pay_type' => $payType,//支付方式：1=微信，2=支付宝，3=转账，4=余额
                'trans_no' => $trans_no ?? "",
            ]);
            if (!$res) {
                bs_log([
                    'order_sn' => $order_sn,
                    'pay_status' => 1,
                    'pay_time' => $pay_time,
                    'check_status' => 1,
                    'check_time' => date('Y-m-d H:i:s'),
                    'pay_type' => $payType,//支付方式：1=微信，2=支付宝，3=转账，4=余额
                    'trans_no' => $trans_no ?? "",
                ], '更新订单失败', '充值回调失败.log');
                return false;
            }
            $creditAction = CreditService::credit_action($order['user_id'], $order['total_money'], [
                'order_sn' => $order_sn,
                'remark' => '充值成功'
            ]);
            if (!$creditAction) {
                bs_log($order_sn, '更新余额失败', '充值回调失败.log');
                return false;
            }

            if (config('point.open') && config('point.recharge')) {
                $point = config('point.recharge');//1元1积分转换
                $point = $order['total_money'] * $point;
                if ($point >= 1) {
                    $pointAction = (new CreditService())->point_action($order['user_id'], $point, [
                        'order_sn' => $order_sn,
                        'remark' => '余额充值赠送'
                    ]);
                }
            }
        } else {
            $order = (new Order())->data_get(['order_sn' => $order_sn]);
            if (!$order) {
                return false;
            }
            if ($order['pay_status'] == 1) {
                return true;
            }
            $res = (new Order())->data_update(['order_sn' => $order_sn], [
                'pay_status' => 1,
                'order_status' => 1,
                'pay_price' => $pay_price,
                'pay_time' => $pay_time,
                'pay_type' => $payType,
                'trans_no' => $trans_no ?? "",
            ]);
            if (!$res) {
                bs_log([
                    'order_sn' => $order_sn,
                    'pay_status' => 1,
                    'order_status' => 1,
                    'pay_price' => $pay_price,
                    'pay_time' => $pay_time,
                    'pay_type' => $payType,
                    'trans_no' => $trans_no ?? "",
                ], '订单支付更新失败', 'alipay_notify.log');
                return false;
            }
            OrderResult::add_order_log([
                'order_id' => $order['order_id'],
                'order_sn' => $order['order_sn'],
                'remark' => '支付成功',
            ]);
            $goodsList = (new OrderGoods())->data_list_nopage(['order_id' => $order['order_id']]);
            //销量增加、库存减少
            foreach ($goodsList as $goods) {
                (new Goods())->data_update(['goods_id' => $goods['goods_id']], [
                    'sales_num' => ['inc', $goods['goods_count']],
                    //'stock' => ['dec', $goods['goods_num']],
                ]);
            }
            if (config('point.open') && config('point.order')) {
                $point = config('point.order');//1元1积分转换
                $point = $order['order_amount'] * $point;
                if ($point >= 1) {
                    $pointAction = (new CreditService())->point_action($order['user_id'], $point, [
                        'order_sn' => $order_sn,
                        'remark' => '订单支付赠送'
                    ]);
                }
            }
        }
        return true;
    }

}
